package com.handyshop.mall.basic.redis.aop;

import com.handyshop.mall.basic.redis.annotation.RedisLock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author hs
 * @date 2021-01-14 17:54
 **/
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class RedisLocksAspect {
    private final RedissonClient redissonClient;

    @Around("@annotation(com.handyshop.mall.basic.redis.annotation.RedisLock)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        Method[] methods = joinPoint.getTarget().getClass().getDeclaredMethods();
        long deadlockTime = 0;
        long tryLockTime = 0;
        String key = null;
        for (Method method : methods) {
            if (methodName.equals(method.getName())) {
                deadlockTime = method.getAnnotation(RedisLock.class).deadlockTime();
                key = method.getAnnotation(RedisLock.class).key();
                tryLockTime = method.getAnnotation(RedisLock.class).tryLockTime();
            }
        }
        RLock lock = redissonClient.getLock(key);
        Object proceed = null;
        boolean cacheRes;
        do {
            cacheRes = lock.tryLock(tryLockTime, deadlockTime, TimeUnit.SECONDS);
            if (cacheRes) {
                log.info("执行程序加锁：method:{}", methodName);
                proceed = joinPoint.proceed();
                lock.unlock();
                log.info("执行程序解锁：method:{}", methodName);
            } else {
                //log.info("执行程序等待中：method:{}", methodName);
                Thread.sleep(20);
            }
        } while (!cacheRes);
        return proceed;
    }

    /**
     * 捕捉业务异常防止死锁
     *
     * @param joinPoint 入参
     */
    @AfterThrowing("@annotation(com.handyshop.mall.basic.redis.annotation.RedisLock)")
    public void doError(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Method[] methods = joinPoint.getTarget().getClass().getDeclaredMethods();
        String key = null;
        for (Method method : methods) {
            if (methodName.equals(method.getName())) {
                key = method.getAnnotation(RedisLock.class).key();
            }
        }
        RLock lock = redissonClient.getLock(key);
        lock.unlock();
    }

}
